/**
 * 
 */
package com.netscenery.yang.model;

import java.util.ArrayList;
import java.util.Stack;

import org.jdom2.Attribute;
import org.jdom2.Element;

import com.netscenery.util.ErrTag;
import com.netscenery.util.Link;
import com.netscenery.util.QName;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangKeyword;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.designer.MapInfo;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author yanzi
 *
 */
public class Grouping extends Entity {
	private DataDefEntity dataDefs;
	private ArrayList<Grouping> groupings;
	private ArrayList<Typedef> typedefs;
	//private Link<DataDefElement> dataDefs;
	
	
	
	

	/**
	 * 
	 */
	public Grouping() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param env
	 * @param name
	 * @throws ModelException 
	 */
	public Grouping(Env env, String name) throws ModelException {
		super(env, name);
		env.setCurGrouping(this);
		dataDefs = new DataDefEntity(newSubEnv());
	}

	/**
	 * @param env
	 * @param qname
	 */
	public Grouping(Env env, QName qname) {
		super(env, qname);
		dataDefs = new DataDefEntity(newSubEnv());
		// TODO Auto-generated constructor stub
	}

	public DataDefEntity getDataDefs() {
		return dataDefs;
	}

	public void setDataDefs(DataDefEntity dataDefs) {
		this.dataDefs = dataDefs;
	}
	public Node createNode(String type,String nodeName) throws ModelException{
		if (null == dataDefs){
			return null;
		}
		return (Node) this.dataDefs.createDataDef(type, nodeName);
	}
	public Uses createUses(String name) throws ModelException{
		if (null == dataDefs){
			return null;
		}
		return (Uses) this.dataDefs.createDataDef(YangKeyword.USES.getKeyword(), name);
	}
	public Uses getUses(String string) {
		if (null == dataDefs){
			return null;
		}
		return dataDefs.getUses(string);
	}


	
	/**
	 * 锟斤拷锟斤拷锟狡达拷锟斤拷一锟斤拷typedef
	 * @param name
	 * @return
	 * @see com.netscenery.model.Typedef
	 */
	public Typedef createTypedef(String name){
		if (null == name){
			return null;
		}
		
		return new Typedef(newSubEnv(),name);
	}
	/**
	 * 锟斤拷锟揭伙拷锟絫ypedef
	 * @param def
	 * @return
	 * @see com.netscenery.model.Typedef
	 */
	public boolean addTypedef(Typedef def){
		if(null == def){
			return false;
		}
		
		if (null == this.typedefs){
			this.typedefs = new ArrayList<Typedef>(1);
		}
		this.typedefs.add(def);
		Env env = this.getEnv();
		env.addDerivedType(def);
		return true;
	}
	/**
	 * 锟斤拷锟斤拷锟脚伙拷取typedef
	 * @param index
	 * @return
	 * @see com.netscenery.model.Typedef
	 */
	public Typedef getTypedef(int index){
		if (null == this.typedefs){
			return null;
		}
		if (index >= this.typedefs.size()){
			return null;
		}
		return this.typedefs.get(index);
	}
	/**
	 * 锟斤拷锟絫ypedef锟斤拷苹锟饺ypedef
	 * @param defName
	 * @return
	 * @see com.netscenery.model.Typedef
	 */
	public Typedef getTypedef(String defName){
		if (null == this.typedefs){
			return null;
		}
		int size = typedefs.size();
		if (size <= 0){
			return null;
		}
		
		for (int i = 0; i < size; i++){
			Typedef def = this.typedefs.get(i);
			if (null != def){
				if (def.getName().equals(defName)){
					return def;
				}
			}
		}
		return null;
	}
	/**
	 * 锟斤拷锟斤拷锟斤拷删锟斤拷typedef
	 * @param index
	 * @return
	 * @see com.netscenery.model.Typedef
	 */
	public Typedef removeTypedef(int index){
		if (null == this.typedefs){
			return null;
		}
		if (index >= this.typedefs.size()){
			return null;
		}
		return this.typedefs.remove(index);
	}
	/**
	 * 锟斤拷锟斤拷锟斤拷删锟斤拷typedef
	 * @param defName
	 * @return
	 */
	public Typedef removeTypedef(String defName){
		if (null == this.typedefs){
			return null;
		}
		int size = typedefs.size();
		if (size <= 0){
			return null;
		}
		
		for (int i = 0; i < size; i++){
			Typedef def = this.typedefs.get(i);
			if (null != def){
				if (def.getName().equals(defName)){
					return typedefs.remove(i);
				}
			}
		}
		return null;
	}
	/**
	 * 锟斤拷取typedef锟侥革拷锟斤拷
	 * @return
	 */
	public int getTypedefsNumber(){
		if (null == this.typedefs){
			return 0;
		}
		return this.typedefs.size();
	}


	/**
	 * 锟斤拷取grouping锟斤拷锟斤拷目
	 * @return
	 * @see com.netscenery.model.Grouping
	 */
	public int getGroupingsNumber(){
		if (null == this.groupings){
			return 0;
		}
		return this.groupings.size();
	}
	/**
	 * 锟斤拷锟街革拷锟斤拷锟斤拷锟狡达拷锟斤拷grouping
	 * @param name
	 * @return
	 * @throws ModelException 
	 * @see com.netscenery.model.Grouping
	 */
	public Grouping createGrouping(String name) throws ModelException{
		if (null == name){
			return null;
		}
		
		return new Grouping(newSubEnv(),name);
	}
	/**
	 * 锟斤拷锟揭伙拷锟絞rouping
	 * @param n
	 * @return
	 * @see com.netscenery.model.Grouping
	 */
	public boolean addGrouping(Grouping n){
		if(null == n){
			return false;
		}
		
		if (null == this.groupings){
			this.groupings = new ArrayList<Grouping>(1);
		}
		//锟斤拷锟杰和憋拷锟斤拷锟絞rouping锟斤拷锟斤拷
		if (this.getName().equals(n.getName())){
			return false;
		}
		if (null != this.getGrouping(n.getName())){
			return false;
		}
		return this.groupings.add(n);
		

	}
	/**
	 * 锟斤拷锟斤拷锟脚伙拷取Grouping
	 * @param index
	 * @return
	 * @see com.netscenery.model.Grouping
	 */
	public Grouping getGrouping(int index){
		if (null == this.groupings){
			return null;
		}
		if (index >= this.groupings.size()){
			return null;
		}
		return this.groupings.get(index);
	}
	/**
	 * 锟斤拷锟斤拷锟狡伙拷取Grouping
	 * @param name
	 * @return
	 * @see com.netscenery.model.Grouping
	 */
	public Grouping getGrouping(String name){
		if (null == this.groupings){
			return null;
		}
		int size = groupings.size();
		if (size <= 0){
			return null;
		}
		
		for (int i = 0; i < size; i++){
			Grouping n = this.groupings.get(i);
			if (null != n){
				if (n.getName().equals(name)){
					return n;
				}
			}
		}
		return null;
	}
	/**
	 * 锟斤拷锟斤拷锟斤拷删锟斤拷Grouping
	 * @param index
	 * @return Grouping
	 * @see com.netscenery.model.Grouping
	 */
	public Grouping removeGrouping(int index){
		if (null == this.groupings){
			return null;
		}
		if (index >= this.groupings.size()){
			return null;
		}
		return this.groupings.remove(index);
	}
	/**
	 * 锟斤拷锟斤拷锟斤拷删锟斤拷Grouping
	 * @param name
	 * @return Grouping
	 * @see com.netscenery.model.Grouping
	 */
	public Grouping removeGrouping(String name){
		if (null == this.groupings){
			return null;
		}
		int size = groupings.size();
		if (size <= 0){
			return null;
		}
		
		
		for (int i = 0; i < size; i++){
			Grouping n = this.groupings.get(i);
			if (null != n){
				if (n.getName().equals(name)){
					return groupings.remove(i);
				}
			}
		}
		return null;
	}
	
	public ArrayList<Node> getSchemaNodes(){
		int size = getDataDefsNumber();
		if (0 == size){
			return null;
		}
		return dataDefs.getSchemaNodes();
		
	}
	
	public ArrayList<Node> getDataNodes(){
		int size = getDataDefsNumber();
		if (0 == size){
			return null;
		}
		return dataDefs.getDataNodes();
		
	}
	
	public DataDefElement getDataDef(int i) {
		if (null == dataDefs){
			return null;
		}
		
		return dataDefs.getDataDef(i);
	}

	public int getDataDefsNumber() {
		if (null == dataDefs){
			return 0;
		}
		return dataDefs.getDataDefsNumber();
	}

	public Node getSon(String nodeName){
		if (null == nodeName){
			return null;
		}
		ArrayList<Node> nodes = getSchemaNodes();
		if (null == nodes){
			return null;
		}
		
		int size = nodes.size();
		
		for (int i = 0; i < size; i++){
			Node n = nodes.get(i);
			if (null == n){
				continue;
			}
			
			if (n.getName().equals(nodeName)){
				return n;
			}
		}
		
		return null;
	}
	public Uses searchUsesIncludeDescendant(Grouping refGrouping){
		Grouping grouping = this;
		
		if(null != grouping){
			Uses uses = grouping.getUses(refGrouping.getQName(env.getCurModule()).toString());
			if (null != uses ){
				return uses;
			}
			
			int groupingsSize = getGroupingsNumber();
			
			for (int i = 0; i < groupingsSize; i++){
				Grouping subGrouping = getGrouping(i);
				if (null != subGrouping){
					Uses subUses = subGrouping.searchUsesIncludeDescendant(refGrouping);
					if (null != subUses){
						return subUses;
					}
				}
			}
		}
		
		return null;
	}
	


	public Stack<Grouping> getAncestorGroupings(){
		Stack<Grouping> groupingStack = new Stack<Grouping>();
		ModelElement parent = env.getParent();
		if (null == parent){
			return null;
		}
		
		if (! (parent instanceof Grouping)){
			return null;
		}
		
		
		Grouping grouping = (Grouping) parent;
		while(null != grouping){
			groupingStack.push(grouping);
			parent = grouping.getEnv().getParent();
			if (null == parent){
				break;
			}
			
			if (! (parent instanceof Grouping)){
				break;
			}
			grouping = (Grouping) parent;
		}
		
		if (groupingStack.isEmpty()){
			return null;
		}
		
		return groupingStack;
	}
	
	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#validate()
	 */
	@Override
	public Validator validate() {
		Validator validator = new Validator();
		//锟斤拷锟斤拷锟斤拷锟角凤拷锟斤拷喜愣拷锟斤拷grouping锟斤拷突
		
		Stack<Grouping> groupStack = getAncestorGroupings();
		if (null != groupStack){
			while(!groupStack.isEmpty()){
				Grouping grouping = groupStack.pop();
				if (null != grouping){
					if (grouping.getName().equals(this.getName())){
						validator.setIsValid(false);
						validator.addMsg(new Message(this,ErrTag.ERROR,"name","loop definition is detected!"));
					}
				}
			}
		}
		
		
		if (0 != this.getGroupingsNumber()){
			for (int i = 0; i < getGroupingsNumber();i++){
				Grouping grouping = getGrouping(i);
				if (null != grouping){
					Validator aValidator = grouping.validate();
					validator.copy(aValidator);
				}
			}
		}
		if (0 != this.getTypedefsNumber()){
			for (int i = 0; i < getTypedefsNumber();i++){
				Typedef typedef = getTypedef(i);
				if (null != typedef){
					Validator aValidator = typedef.validate();
					validator.copy(aValidator);
				}
			}
		}
		if (0 != this.getDataDefsNumber()){
			for (int i = 0; i < getDataDefsNumber();i++){
				DataDefElement dataDef = getDataDef(i);
				if (null != dataDef){
					Validator aValidator = dataDef.validate();
					validator.copy(aValidator);
				}
			}
		}
		
		Validator sValidator = super.validate();
		validator.copy(sValidator);
		return validator;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYang()
	 */
	@Override
	public YangStatement toYang() {
		YangStatement grouping = new YangStatement(YangKeyword.GROUPING.getKeyword(),this.name);
		
		if (!isNullString(getDescription())){
			YangStatement descr = new YangStatement(YangKeyword.DESCRIPTION.getKeyword(),getDescription());
			grouping.addChild(descr);
		}
		if (null != getReference()){
			YangStatement ref = new YangStatement(YangStatement.YANG_STATEMENT_REFERENCE,this.getReference());
			grouping.addChild(ref);
		}
		
		if (Status.CURRENT != getStatus()){
			YangStatement status = new YangStatement(YangStatement.YANG_STATEMENT_STATUS,this.getStatus().getStatus());
			grouping.addChild(status);
		}
		/*
		 * typedefs
		 */
		if (null != this.typedefs){
			int size = this.getTypedefsNumber();
			for (int i = 0; i < size; i++){
				Typedef def = this.getTypedef(i);
				if (null != def){
					YangStatement defstat = def.writeYang();
					if (null == defstat){
						continue;
					}
					grouping.addChild(defstat);
				}
			}
		}
		
		/*
		 * groupings
		 */
		if (null != this.groupings){
			int size = this.getGroupingsNumber();
			for (int i = 0; i < size; i++){
				Grouping grp = this.getGrouping(i);
				if (null != grp){
					YangStatement grpstat = grp.writeYang();
					if (null == grpstat){
						continue;
					}
					grouping.addChild(grpstat);
				}
			}
		}
		
		/*
		 * nodes
		 */
		if (null != this.dataDefs){
			int size = this.getDataDefsNumber();
			for (int i = 0; i < size; i++){
				DataDefElement n = this.getDataDef(i);
				if (null != n){
					YangStatement nodestat = n.writeYang();
					if (null == nodestat){
						continue;
					}
					grouping.addChild(nodestat);
				}
			}
		}
		
		return grouping;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYin()
	 */
	@Override
	public Element toYin() {
		Element grouping = new Element(YangKeyword.GROUPING.getKeyword(),env.getNs());
		Attribute name = new Attribute(XMLTag.XML_TAG_NAME,this.name);
		grouping.setAttribute(name);
		
		if (!isNullString(getDescription())){
			Element descr = new Element(YangStatement.YANG_STATEMENT_DESCRIPTION,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(getDescription());
			descr.addContent(text);
			grouping.addContent(descr);
		}
		if (null != getReference()){
			Element ref = new Element(YangStatement.YANG_STATEMENT_REFERENCE,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(getReference());
			ref.addContent(text);
			grouping.addContent(ref);
		}
		
		if (Status.CURRENT != getStatus()){
			Element status = new Element(YangStatement.YANG_STATEMENT_STATUS,env.getNs());
			Attribute value = new Attribute(YangKeyword.VALUE.getKeyword(),this.getStatus().getStatus());
			status.setAttribute(value);
			grouping.addContent(status);
		}
		
		/*
		 * typedefs
		 */
		if (null != this.typedefs){
			int size = this.getTypedefsNumber();
			for (int i = 0; i < size; i++){
				Typedef def = this.getTypedef(i);
				if (null != def){
					Element defstat = def.writeYin();
					if (null == defstat){
						continue;
					}
					grouping.addContent(defstat);
				}
			}
		}
		/*
		 * groupings
		 */
		if (null != this.groupings){
			int size = this.getGroupingsNumber();
			for (int i = 0; i < size; i++){
				Grouping grp = this.getGrouping(i);
				if (null != grp){
					Element grpstat = grp.writeYin();
					if (null == grpstat){
						continue;
					}
					grouping.addContent(grpstat);
				}
			}
		}

		//datadefs
		if (null != this.dataDefs){
			int size = this.getDataDefsNumber();
			for (int i = 0; i < size; i++){
				DataDefElement n = this.getDataDef(i);
				if (null != n){
					Element node = n.writeYin();
					if (null == node){
						continue;
					}
					grouping.addContent(node);
				}
			}
		}	

		
		return grouping;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toXML()
	 */
	@Override
	public Element toXML() {
		// TODO Auto-generated method stub
		return null;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readXML(org.jdom2.Element)
	 */
	@Override
	public int readXML(Element element) throws ModelException {
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readYin(org.jdom2.Element)
	 */
	@Override
	public int readYin(Element element) throws ModelException {
		if (null == element){
			return MODELELEMENT_OK;
		}

		// 锟斤拷锟斤拷
		if (element.getName()
				.equals(YangStatement.YANG_STATEMENT_DESCRIPTION)) {
			Element text = element.getChild(XMLTag.XML_TAG_TEXT, env.getNs());
			if (null == text) {
				return MODELELEMENT_FAIL;
			}
			this.setDescription(text.getText());
		}
		// reference
		else if (element.getName().equals(
				YangStatement.YANG_STATEMENT_REFERENCE)) {

			Element text = element.getChild(XMLTag.XML_TAG_TEXT, env.getNs());
			if (null == text) {
				return MODELELEMENT_FAIL;
			}
			this.setReference(text.getText());
		}
		// status
		else if (element.getName()
				.equals(YangStatement.YANG_STATEMENT_STATUS)) {
			String value = element.getAttributeValue(YangKeyword.VALUE
					.getKeyword());
			if (null == value) {
				return MODELELEMENT_FAIL;
			} else {
				this.status = Status.getStatus(value);
			}

		}
		
		/*
		 * typedefs
		 */
		else if (element.getName().equals(YangStatement.YANG_STATEMENT_TYPEDEF)){

			Env subEnv = newSubEnv();
			Typedef ext = (Typedef) ModelElement.parseYin(subEnv, element);
			this.addTypedef(ext);
		}
		/*
		 * groupings
		 */
		else if (element.getName().equals(YangStatement.YANG_STATEMENT_GROUPING)){

			Env subEnv = newSubEnv();
			Grouping ext = (Grouping) ModelElement.parseYin(subEnv, element);
			this.addGrouping(ext);
		}

		//datadefs
		else if (element.getName().equals(YangStatement.YANG_STATEMENT_CONTAINER)
				|| element.getName().equals(YangStatement.YANG_STATEMENT_CHOICE)
				|| element.getName().equals(YangStatement.YANG_STATEMENT_LEAF)
				|| element.getName().equals(YangStatement.YANG_STATEMENT_LEAFLIST)
				|| element.getName().equals(YangStatement.YANG_STATEMENT_LIST)
				|| element.getName().equals(YangStatement.YANG_STATEMENT_ANYXML)
				|| element.getName().equals(YangStatement.YANG_STATEMENT_USES)){

			Env subEnv = newSubEnv();
			Node n = (Node) ModelElement.parseYin(subEnv, element);
			this.addDataDef(n);
		} 
		else {
			return MODELELEMENT_FAIL;
		}
		return MODELELEMENT_OK;
	}
	
	public  boolean addDataDef(DataDefElement n) {
		if (null == dataDefs){
			dataDefs = new DataDefEntity(newSubEnv());
		}
		
		return dataDefs.addDataDef(n);
		
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readYANG(com.netscenery.yang.YangStatement)
	 */
	@Override
	public int readYANG(YangStatement statement) throws YangParserException, ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}
		String value = statement.getArgument();
		
		//锟斤拷锟斤拷
		if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_DESCRIPTION)){
			this.setDescription(value);
		}
	    //reference
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_REFERENCE)){
			this.setReference(value);
		}
	    //status
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_STATUS)){
			this.setStatus(Status.getStatus(value));
		}
		/*
		 * typedefs
		 */
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_TYPEDEF)){

			Env subEnv = newSubEnv();
			Typedef ext = (Typedef) ModelElement.parseYANG(subEnv, statement);
			this.addTypedef(ext);
		}
		/*
		 * groupings
		 */
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_GROUPING)){

			Env subEnv = newSubEnv();
			Grouping ext = (Grouping) ModelElement.parseYANG(subEnv, statement);
			this.addGrouping(ext);
		}

		//datadefs
		else if (statement.getKeyword().equals(YangStatement.YANG_STATEMENT_CONTAINER)
				|| statement.getKeyword().equals(YangStatement.YANG_STATEMENT_CHOICE)
				|| statement.getKeyword().equals(YangStatement.YANG_STATEMENT_LEAF)
				|| statement.getKeyword().equals(YangStatement.YANG_STATEMENT_LEAFLIST)
				|| statement.getKeyword().equals(YangStatement.YANG_STATEMENT_LIST)
				|| statement.getKeyword().equals(YangStatement.YANG_STATEMENT_ANYXML)
				|| statement.getKeyword().equals(YangStatement.YANG_STATEMENT_USES)
				){
			Env subEnv = newSubEnv();
			DataDefElement n = (DataDefElement) ModelElement.parseYANG(subEnv, statement);
			this.addDataDef(n);
		}   
		else {
			return MODELELEMENT_FAIL;
		}
		
		return MODELELEMENT_OK;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#isFieldMandatory(java.lang.String)
	 */
	@Override
	public boolean isFieldMandatory(String field) {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#getMapInfo()
	 */
	@Override
	public MapInfo getMapInfo() throws SecurityException, NoSuchMethodException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void update() {
		// TODO Auto-generated method stub
		
	}

}
